var gadgets = gadgets || {};
gadgets.rpc = function () {
    var CALLBACK_NAME = "__cb";
    var DEFAULT_NAME = "";
    var FE_G2C_CHANNEL = "__g2c_rpc";
    var FE_C2G_CHANNEL = "__c2g_rpc";
    var services = {};
    var iframePool = [];
    var relayUrl = {};
    var useLegacyProtocol = {};
    var authToken = {};
    var callId = 0;
    var callbacks = {};
    var setup = {};
    var sameDomain = {};
    var params = {};
    if (gadgets.util) {
        params = gadgets.util.getUrlParameters()
    }
    authToken[".."] = params.rpctoken || params.ifpctok || 0;

    function getRelayChannel() {
        return typeof window.postMessage === "function" ? "wpm" : typeof document.postMessage === "function" ? "dpm" : navigator.product === "Gecko" ? "fe" : "ifpc"
    }
    function setupChannel() {
        if (relayChannel === "dpm" || relayChannel === "wpm") {
            window.addEventListener("message", function (packet) {
                process(gadgets.json.parse(packet.data))
            }, false)
        }
    }
    var relayChannel = getRelayChannel();
    setupChannel();
    services[DEFAULT_NAME] = function () {
        throw new Error("Unknown RPC service: " + this.s)
    };
    services[CALLBACK_NAME] = function (callbackId, result) {
        var callback = callbacks[callbackId];
        if (callback) {
            delete callbacks[callbackId];
            callback(result)
        }
    };

    function setupFrame(frameId) {
        if (setup[frameId]) {
            return
        }
        if (relayChannel === "fe") {
            try {
                var frame = document.getElementById(frameId);
                frame[FE_G2C_CHANNEL] = function (args) {
                    process(gadgets.json.parse(args))
                }
            } catch (e) {}
        }
        setup[frameId] = true
    }
    function encodeLegacyData(args) {
        var stringify = gadgets.json.stringify;
        var argsEscaped = [];
        for (var i = 0, j = args.length; i < j; ++i) {
            argsEscaped.push(encodeURIComponent(stringify(args[i])))
        }
        return argsEscaped.join("&")
    }
    function process(rpc) {
        if (rpc && typeof rpc.s === "string" && typeof rpc.f === "string" && rpc.a instanceof Array) {
            if (authToken[rpc.f]) {
                if (authToken[rpc.f] != rpc.t) {
                    throw new Error("Invalid auth token.")
                }
            }
            if (rpc.c) {
                rpc.callback = function (result) {
                    gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result)
                }
            }
            var result = (services[rpc.s] || services[DEFAULT_NAME]).apply(rpc, rpc.a);
            if (rpc.c && typeof result != "undefined") {
                gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result)
            }
        }
    }
    function callFrameElement(targetId, serviceName, from, rpcData, callArgs) {
        try {
            if (from != "..") {
                var fe = window.frameElement;
                if (typeof fe[FE_G2C_CHANNEL] === "function") {
                    if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !== "function") {
                        fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function (args) {
                            process(gadgets.json.parse(args))
                        }
                    }
                    fe[FE_G2C_CHANNEL](rpcData);
                    return
                }
            } else {
                var frame = document.getElementById(targetId);
                if (typeof frame[FE_G2C_CHANNEL] === "function" && typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] === "function") {
                    frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
                    return
                }
            }
        } catch (e) {}
        callIfpc(targetId, serviceName, from, rpcData, callArgs)
    }
    function callIfpc(targetId, serviceName, from, rpcData, callArgs) {
        var relay = gadgets.rpc.getRelayUrl(targetId);
        if (!relay) {
            throw new Error("No relay file assigned for IFPC")
        }
        var src = null;
        if (useLegacyProtocol[targetId]) {
            src = [relay, "#", encodeLegacyData([from, callId, 1, 0, encodeLegacyData([from, serviceName, "", "", from].concat(callArgs))])].join("")
        } else {
            src = [relay, "#", targetId, "&", from, "@", callId, "&1&0&", encodeURIComponent(rpcData)].join("")
        }
        emitInvisibleIframe(src)
    }
    function emitInvisibleIframe(src) {
        var iframe;
        for (var i = iframePool.length - 1; i >= 0; --i) {
            var ifr = iframePool[i];
            try {
                if (ifr && (ifr.recyclable || ifr.readyState === "complete")) {
                    ifr.parentNode.removeChild(ifr);
                    if (window.ActiveXObject) {
                        iframePool[i] = ifr = null;
                        iframePool.splice(i, 1)
                    } else {
                        ifr.recyclable = false;
                        iframe = ifr;
                        break
                    }
                }
            } catch (e) {}
        }
        if (!iframe) {
            iframe = document.createElement("iframe");
            iframe.style.border = iframe.style.width = iframe.style.height = "0px";
            iframe.style.visibility = "hidden";
            iframe.style.position = "absolute";
            iframe.onload = function () {
                this.recyclable = true
            };
            iframePool.push(iframe)
        }
        iframe.src = src;
        setTimeout(function () {
            document.body.appendChild(iframe)
        }, 0)
    }
    function callSameDomain(target, rpc) {
        if (typeof sameDomain[target] === "undefined") {
            sameDomain[target] = false;
            var targetEl = null;
            if (target === "..") {
                targetEl = parent
            } else {
                targetEl = frames[target]
            }
            try {
                sameDomain[target] = targetEl.gadgets.rpc.receiveSameDomain
            } catch (e) {}
        }
        if (typeof sameDomain[target] === "function") {
            sameDomain[target](rpc);
            return true
        }
        return false
    }
    if (gadgets.config) {
        function init(config) {
            if (config.rpc.parentRelayUrl.substring(0, 7) === "http://") {
                relayUrl[".."] = config.rpc.parentRelayUrl
            } else {
                var params = document.location.search.substring(0).split("&");
                var parentParam = "";
                for (var i = 0, param; param = params[i]; ++i) {
                    if (param.indexOf("parent=") === 0) {
                        parentParam = decodeURIComponent(param.substring(7));
                        break
                    }
                }
                relayUrl[".."] = parentParam + config.rpc.parentRelayUrl
            }
            useLegacyProtocol[".."] = !! config.rpc.useLegacyProtocol
        }
        var requiredConfig = {
            parentRelayUrl: gadgets.config.NonEmptyStringValidator
        };
        gadgets.config.register("rpc", requiredConfig, init)
    }
    return {
        register: function (serviceName, handler) {
            if (serviceName == CALLBACK_NAME) {
                throw new Error("Cannot overwrite callback service")
            }
            if (serviceName == DEFAULT_NAME) {
                throw new Error("Cannot overwrite default service:" + " use registerDefault")
            }
            services[serviceName] = handler
        },
        unregister: function (serviceName) {
            if (serviceName == CALLBACK_NAME) {
                throw new Error("Cannot delete callback service")
            }
            if (serviceName == DEFAULT_NAME) {
                throw new Error("Cannot delete default service:" + " use unregisterDefault")
            }
            delete services[serviceName]
        },
        registerDefault: function (handler) {
            services[""] = handler
        },
        unregisterDefault: function () {
            delete services[""]
        },
        call: function (targetId, serviceName, callback, var_args) {
            ++callId;
            targetId = targetId || "..";
            if (callback) {
                callbacks[callId] = callback
            }
            var from = "..";
            if (targetId === "..") {
                from = window.name
            }
            var rpc = {
                s: serviceName,
                f: from,
                c: callback ? callId : 0,
                a: Array.prototype.slice.call(arguments, 3),
                t: authToken[targetId]
            };
            if (callSameDomain(targetId, rpc)) {
                return
            }
            var rpcData = gadgets.json.stringify(rpc);
            var channelType = relayChannel;
            if (useLegacyProtocol[targetId]) {
                channelType = "ifpc"
            }
            switch (channelType) {
            case "dpm":
                var targetDoc = targetId === ".." ? parent.document : frames[targetId].document;
                targetDoc.postMessage(rpcData);
                break;
            case "wpm":
                var targetWin = targetId === ".." ? parent : frames[targetId];
                targetWin.postMessage(rpcData, relayUrl[targetId]);
                break;
            case "fe":
                callFrameElement(targetId, serviceName, from, rpcData, rpc.a);
                break;
            default:
                callIfpc(targetId, serviceName, from, rpcData, rpc.a);
                break
            }
        },
        getRelayUrl: function (targetId) {
            return relayUrl[targetId]
        },
        setRelayUrl: function (targetId, url, opt_useLegacy) {
            relayUrl[targetId] = url;
            useLegacyProtocol[targetId] = !! opt_useLegacy
        },
        setAuthToken: function (targetId, token) {
            authToken[targetId] = token;
            setupFrame(targetId)
        },
        getRelayChannel: function () {
            return relayChannel
        },
        receive: function (fragment) {
            if (fragment.length > 4) {
                process(gadgets.json.parse(decodeURIComponent(fragment[fragment.length - 1])))
            }
        },
        receiveSameDomain: function (rpc) {
            rpc.a = Array.prototype.slice.call(rpc.a);
            window.setTimeout(function () {
                process(rpc)
            }, 0)
        }
    }
}();
Function.prototype.inherits = function (parentCtor) {
    function tempCtor() {}
    tempCtor.prototype = parentCtor.prototype;
    this.superClass_ = parentCtor.prototype;
    this.prototype = new tempCtor();
    this.prototype.constructor = this
};
var gadgets = gadgets || {};
gadgets.json = function () {
    function f(n) {
        return n < 10 ? "0" + n : n
    }
    Date.prototype.toJSON = function () {
        return [this.getUTCFullYear(), "-", f(this.getUTCMonth() + 1), "-", f(this.getUTCDate()), "T", f(this.getUTCHours()), ":", f(this.getUTCMinutes()), ":", f(this.getUTCSeconds()), "Z"].join("")
    };
    var m = {
        "\b": "\\b",
        "\t": "\\t",
        "\n": "\\n",
        "\f": "\\f",
        "\r": "\\r",
        '"': '\\"',
        "\\": "\\\\"
    };

    function stringify(value) {
        var a, i, k, l, r = /["\\\x00-\x1f\x7f-\x9f]/g,
            v;
        switch (typeof value) {
        case "string":
            return r.test(value) ? '"' + value.replace(r, function (a) {
                var c = m[a];
                if (c) {
                    return c
                }
                c = a.charCodeAt();
                return "\\u00" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)
            }) + '"' : '"' + value + '"';
        case "number":
            return isFinite(value) ? String(value) : "null";
        case "boolean":
        case "null":
            return String(value);
        case "object":
            if (!value) {
                return "null"
            }
            a = [];
            if (typeof value.length === "number" && !(value.propertyIsEnumerable("length"))) {
                l = value.length;
                for (i = 0; i < l; i += 1) {
                    a.push(stringify(value[i]) || "null")
                }
                return "[" + a.join(",") + "]"
            }
            for (k in value) {
                if (value.hasOwnProperty(k)) {
                    if (typeof k === "string") {
                        v = stringify(value[k]);
                        if (v) {
                            a.push(stringify(k) + ":" + v)
                        }
                    }
                }
            }
            return "{" + a.join(",") + "}"
        }
    }
    return {
        stringify: stringify,
        parse: function (text) {
            if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/b-u]/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) {
                return eval("(" + text + ")")
            }
            return false
        }
    }
}();
var gadgets = gadgets || {};
gadgets.error = {};
gadgets.error.SUBCLASS_RESPONSIBILITY = "subclass responsibility";
gadgets.error.TO_BE_DONE = "to be done";
gadgets.log = function (message) {
    if (window.console && console.log) {
        console.log(message)
    } else {
        var logEntry = document.createElement("div");
        logEntry.className = "gadgets-log-entry";
        logEntry.innerHTML = message;
        document.body.appendChild(logEntry)
    }
};
gadgets.Extensible = function () {};
gadgets.Extensible.prototype.setDependencies = function (dependencies) {
    for (var p in dependencies) {
        this[p] = dependencies[p]
    }
};
gadgets.Extensible.prototype.getDependencies = function (name) {
    return this[name]
};
gadgets.UserPrefStore = function () {};
gadgets.UserPrefStore.prototype.getPrefs = function (gadget) {
    throw Error(gadgets.error.SUBCLASS_RESPONSIBILITY)
};
gadgets.UserPrefStore.prototype.savePrefs = function (gadget) {
    throw Error(gadgets.error.SUBCLASS_RESPONSIBILITY)
};
gadgets.DefaultUserPrefStore = function () {
    gadgets.UserPrefStore.call(this)
};
gadgets.DefaultUserPrefStore.inherits(gadgets.UserPrefStore);
gadgets.DefaultUserPrefStore.prototype.getPrefs = function (gadget) {};
gadgets.DefaultUserPrefStore.prototype.savePrefs = function (gadget) {};
gadgets.GadgetService = function () {};
gadgets.GadgetService.prototype.setHeight = function (elementId, height) {
    throw Error(gadgets.error.SUBCLASS_RESPONSIBILITY)
};
gadgets.GadgetService.prototype.setTitle = function (gadget, title) {
    throw Error(gadgets.error.SUBCLASS_RESPONSIBILITY)
};
gadgets.GadgetService.prototype.setUserPref = function (id) {
    throw Error(gadgets.error.SUBCLASS_RESPONSIBILITY)
};
gadgets.IfrGadgetService = function () {
    gadgets.GadgetService.call(this);
    var service = this;
    gadgets.rpc.register("resize_iframe", function () {
        service.setHeight.apply(this, arguments)
    });
    gadgets.rpc.register("set_pref", function () {
        service.setUserPref.apply(this, arguments)
    });
    gadgets.rpc.register("set_title", function () {
        service.setTitle.apply(this, arguments)
    });
    gadgets.rpc.register("requestNavigateTo", function () {
        service.requestNavigateTo.apply(this, arguments)
    })
};
gadgets.IfrGadgetService.inherits(gadgets.GadgetService);
gadgets.IfrGadgetService.prototype.setHeight = function (height) {
    if (height > gadgets.container.maxheight_) {
        height = gadgets.container.maxheight_
    }
    var element = document.getElementById(this.f);
    if (element) {
        element.style.height = height + "px"
    }
};
gadgets.IfrGadgetService.prototype.requestNavigateTo = function (view, opt_params) {
    var id = this.getGadgetIdFromModuleId(this.f);
    var url = this.getUrlForView(view);
    if (opt_params) {
        var paramStr = JSON.stringify(opt_params);
        if (paramStr.length > 0) {
            url += "&appParams=" + encodeURIComponent(paramStr)
        }
    }
    if (url && document.location.href.indexOf(url) == -1) {
        document.location.href = url
    }
};
gadgets.IfrGadgetService.prototype.getUrlForView = function (view) {
    if (view === "canvas") {
        return "/canvas"
    } else {
        if (view === "profile") {
            return "/profile"
        } else {
            return null
        }
    }
};
gadgets.IfrGadgetService.prototype.getGadgetIdFromModuleId = function (moduleId) {
    return parseInt(moduleId.match(/_([0-9]+)$/)[1], 10)
};
gadgets.LayoutManager = function () {};
gadgets.LayoutManager.prototype.getGadgetChrome = function (gadget) {
    throw Error(gadgets.error.SUBCLASS_RESPONSIBILITY)
};
gadgets.StaticLayoutManager = function () {
    gadgets.LayoutManager.call(this)
};
gadgets.StaticLayoutManager.inherits(gadgets.LayoutManager);
gadgets.StaticLayoutManager.prototype.setGadgetChromeIds = function (gadgetChromeIds) {
    this.gadgetChromeIds_ = gadgetChromeIds
};
gadgets.StaticLayoutManager.prototype.getGadgetChrome = function (gadget) {
    var chromeId = this.gadgetChromeIds_[gadget.id];
    return chromeId ? document.getElementById(chromeId) : null
};
gadgets.FloatLeftLayoutManager = function (layoutRootId) {
    gadgets.LayoutManager.call(this);
    this.layoutRootId_ = layoutRootId
};
gadgets.FloatLeftLayoutManager.inherits(gadgets.LayoutManager);
gadgets.FloatLeftLayoutManager.prototype.getGadgetChrome = function (gadget) {
    var layoutRoot = document.getElementById(this.layoutRootId_);
    if (layoutRoot) {
        var chrome = document.createElement("div");
        chrome.className = "gadgets-gadget-chrome";
        chrome.style.cssFloat = "left";
        layoutRoot.appendChild(chrome);
        return chrome
    } else {
        return null
    }
};